package multi_thread.advanced;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author Cheng Jun
 * Description: 解决 原子类 ABA问题的 带版本号的原子类
 * AtomicStampedReference 原理是用一个 int类型的stamp 表示版本号，将 value 和 stamp 封装成 pair 对象，
 * 然后利用 sun.misc.Unsafe#compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object) 比较替换对象
 * @version 1.0
 * @date 2021/9/24 23:48
 * @see AtomicBooleanExample
 */
public class AtomicStampedReferenceExample {

    private static AtomicStampedReference<Boolean> OPEN_OR_CLOSE = new AtomicStampedReference<>(false, 0);

    public static void main(String[] args) throws InterruptedException {
        boolean expectedReference = OPEN_OR_CLOSE.getReference();
        boolean newReference = true;
        int expectedStamp = OPEN_OR_CLOSE.getStamp();

        Thread zhangsan = new Thread(() -> {
            boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expectedReference, newReference, expectedStamp, expectedStamp + 1);
            System.out.println(Thread.currentThread().getName() + "开柜门：" + isOpen);
            // 省略 偷试题的操作
            boolean isClose = OPEN_OR_CLOSE.compareAndSet(newReference, expectedReference, OPEN_OR_CLOSE.getStamp(), OPEN_OR_CLOSE.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "关柜门：" + isClose);
            System.out.println(Thread.currentThread().getName() + "偷题是否成功" + (isOpen && isClose));
        }, "zhangsan");

        Thread lisi = new Thread(() -> {
            try {
                // 张三使手段，确保自己先执行完，真实场景可能用其它的手段
                zhangsan.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "打开之前门状态为：" + OPEN_OR_CLOSE.getReference());
            boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expectedReference, newReference, expectedStamp, expectedStamp + 1);
            System.out.println(Thread.currentThread().getName() + "打开之后门状态为：" + OPEN_OR_CLOSE.getReference() + ", "
                    + Thread.currentThread().getName() + "开柜门是否成功：" + isOpen);
        }, "lisi");

        // 当然这个地方最好用 发令枪做，同时起跑
        zhangsan.start();
        lisi.start();
    }

}
